#include "core/Logger.h"

#include <iostream>
#include <iomanip>
#include <chrono>
#include <ctime>
#include <sstream>
#include <unistd.h>
#include <system_error>

namespace ems {
namespace core {

Logger& Logger::getInstance() {
    static Logger instance;
    return instance;
}

Logger::~Logger() {
    if (logFile_.is_open()) {
        logFile_.close();
    }
}

void Logger::initialize(const std::string& logFile, LogLevel level) {
    std::lock_guard<std::mutex> lock(mutex_);
    
    level_ = level;
    
    if (logFile_.is_open()) {
        logFile_.close();
    }
    
    if (!logFile.empty()) {
        logFile_.open(logFile, std::ios::app);
        if (!logFile_.is_open()) {
            std::cerr << "Failed to open log file: " << logFile << std::endl;
        }
    }
    
    initialized_ = true;
}

void Logger::log(LogLevel level, const std::string& message, const std::string& file, int line) {
    if (level < level_) {
        return;
    }
    
    std::unique_lock<std::mutex> lock(mutex_, std::defer_lock);
    try {
        lock.lock();
    } catch (const std::system_error& e) {
        std::cerr << "[Logger::log] mutex lock failed: " << e.what() << ", fallback to stderr" << std::endl;
        std::cerr << message << std::endl;
        return;
    }

    std::string timestamp = getCurrentTime();
    std::string levelStr = levelToString(level);
    
    std::ostringstream oss;
    oss << "[" << timestamp << "] [" << levelStr << "] ";
    
    if (!file.empty() && line > 0) {
        size_t pos = file.find_last_of("/\\");
        std::string filename = (pos != std::string::npos) ? file.substr(pos + 1) : file;
        oss << "[" << filename << ":" << line << "] ";
    }
    
    oss << message << std::endl;
    
    std::string logMessage = oss.str();
    
    std::cout << logMessage;
    
    if (logFile_.is_open()) {
        logFile_ << logMessage;
        logFile_.flush();
    }
}

void Logger::logTagged(LogLevel level, const std::string& message, const std::string& file, int line, LogTag tag) {
    if (level < level_) {
        return;
    }

    std::unique_lock<std::mutex> lock(mutex_, std::defer_lock);
    try {
        lock.lock();
    } catch (const std::system_error& e) {
        std::cerr << "[Logger::logTagged] mutex lock failed: " << e.what() << ", fallback to stderr" << std::endl;
        std::cerr << message << std::endl;
        return;
    }

    std::string timestamp = getCurrentTime();
    std::string levelStr = levelToString(level);

    std::ostringstream oss;
    oss << "[" << timestamp << "] [" << levelStr << "] ";

    if (!file.empty() && line > 0) {
        size_t pos = file.find_last_of("/\\");
        std::string filename = (pos != std::string::npos) ? file.substr(pos + 1) : file;
        oss << "[" << filename << ":" << line << "] ";
    }

    oss << message << std::endl;

    std::string logMessage = oss.str();

    // 控制台彩色输出（仅当是 TTY 时启用）
    bool useColor = isatty(STDOUT_FILENO);
    const char* reset = "\033[0m";
    const char* color = nullptr;
    if (useColor) {
        switch (tag) {
            case LogTag::MQTT_SEND: color = "\033[32m"; break; // 绿色：发送
            case LogTag::MQTT_RECV: color = "\033[36m"; break; // 青色：接收
            default: color = nullptr; break;
        }
    }

    if (color) {
        std::cout << color << logMessage << reset;
    } else {
        std::cout << logMessage;
    }

    if (logFile_.is_open()) {
        // 文件中不写入颜色控制码
        logFile_ << logMessage;
        logFile_.flush();
    }
}

std::string Logger::levelToString(LogLevel level) const {
    switch (level) {
        case LogLevel::DEBUG: return "DEBUG";
        case LogLevel::INFO:  return "INFO ";
        case LogLevel::WARN:  return "WARN ";
        case LogLevel::ERROR: return "ERROR";
        case LogLevel::FATAL: return "FATAL";
        default: return "UNKNOWN";
    }
}

std::string Logger::getCurrentTime() const {
    auto now = std::chrono::system_clock::now();
    auto time_t = std::chrono::system_clock::to_time_t(now);
    auto ms = std::chrono::duration_cast<std::chrono::milliseconds>(
        now.time_since_epoch()) % 1000;
    
    std::ostringstream oss;
    oss << std::put_time(std::localtime(&time_t), "%Y-%m-%d %H:%M:%S");
    oss << '.' << std::setfill('0') << std::setw(3) << ms.count();
    
    return oss.str();
}

}  // namespace core
}  // namespace ems
